

#include "xmodule_config.h"
#include "rtl876x_spic.h"
#if MODULE_CHECK_ENABLED( LCD )

#include <string.h>
#include "xgui_config.h"
#include "lcd_internal.h"
#include "xDLPS.h"

#if MODULE_CHECK_LOG_ENABLED( LCD )
#include "log.h"
#define LOG     LOG_INFO
#define LOG0    LOG_INFO0 
#define LOG1    LOG_INFO1
#define LOG2    LOG_INFO2
#define LOG3    LOG_INFO3
#else 
#define LOG(...) 
#define LOG0(...)
#define LOG1(...)
#define LOG2(...)
#define LOG3(...)
#endif 

#if !MODULE_CHECK_ENABLED( LCD_QSPI )
#error "must define qspi lcd interface!!!"
#endif

//static bool is_initialized = false; 
static uint8_t lcd_dlps_index;

#include LCD_DRIVER_FILE

volatile lcd_write_ctl_t lcd_wrctl; 



static const T_IO_MODULE_CBS lcd_cbs = 
{
    .init = NULL,
    .enter_dlps = em_lcd_enter_dlps,
    .exit_dlps = em_lcd_exit_dlps,
    .check_allow_dlps = NULL,
    .system_wakeup_check_allow_dlps = NULL, 
    .name = "lcd\0",
};


GUI_RAM_CODE
static void spic2_spi_write(uint8_t *buf, uint32_t len)
{
    uint32_t timestamp=em_rtc_get_ticks();
    SPIC2->ser = BIT0;
    SPIC2->ssienr = 0x00;//disable SPIC2

    SPIC2->ctrlr0 &= ~(BIT_CMD_CH(3) | BIT_ADDR_CH(3) | BIT_DATA_CH(3));//SET CHANNEL NUM
    SPIC2->ctrlr0 &= ~(BIT_TMOD(3)); //tx mode

    for (uint32_t i = 0; i < len; i++)
    {
        SPIC2->dr[0].byte = buf[i];
    }
    SPIC2->ssienr = 0x01;//enable SPIC2

    while ((SPIC2->sr & BIT0) && !em_rtc_check_time_expired( timestamp, EM_RTC_TOTICKS(500) )); // wait bus busy

    SPIC2->ssienr = 0x00;//disable SPIC2
}

GUI_RAM_CODE
void lcd_qspi_write_cmd_para(uint16_t cmd , uint8_t *data , uint8_t data_len) //total 4 byte, first byte is 0x02
{
    static uint8_t sdat[40];// = {0x02, 0x00, cmd, 0x00 };
    
    if((data_len+4) > sizeof(sdat)) 
    {
        LOG("==>lcd para too long!!!");
        return;
    }
    
    sdat[0] = 0x02;
    sdat[1] = 0x00;
    sdat[2] = cmd;
    sdat[3] = 0x00;
    
    if(data && data_len>0)
    {
        for(uint8_t i =0 ; i<data_len ; i++)
        sdat[4+i]=data[i];
    }
//    else if(0 == data_len)
//    {
//        data_len = 1;
//        sdat[4]  = 0x00;
//    }
    
    spic2_spi_write(sdat, data_len+4);
}

DATA_RAM_FUNCTION
void lcd_qspi_enter_data_output_mode(void) //total 4 byte, first byte is 0x32
{
    SPIC2->ser = BIT0;//select CS0
    SPIC2->ssienr = 0x00;//disable SPIC2

    SPIC2->ctrlr0 &= ~(BIT_CMD_CH(3) | BIT_ADDR_CH(3) | BIT_DATA_CH(3));//SET CHANNEL NUM
    SPIC2->ctrlr0 |= (BIT_CMD_CH(0) | BIT_ADDR_CH(0) | BIT_DATA_CH(2));
    SPIC2->ctrlr0 &= ~(BIT_TMOD(3)); //set tx mode

    SPIC2->imr |= BIT_TXSIM;
    SPIC2->ctrlr2 |= BIT_SEQ_EN;

    /* must push cmd and address before SPIC enable */
    uint32_t first_word = 0x32 | __REV(0x002c00);
    SPIC2->dr[0].word = first_word;

    SPIC2->dmacr = 2;

    /* change this value can not influence the result. the wave is split into two periods. the first is 32 bytes. */
    SPIC2->dmatdlr = 4; /* no any influence. */



    SPIC2->ssienr = 0x01;//enable SPIC2
    /*then , we can push data to FIFO*/


    //SPIC2->ctrlr2 &= ~ BIT_SEQ_EN;
    //SPIC2->imr &= ~ BIT_TXSIM;

    //SPIC2->ssienr = 0x00;//disable SPIC2
}

GUI_RAM_CODE
void LCD_DMA_IRQ_HANDLER(void)
{
    if( GDMA_GetTransferINTStatus( LCD_DMA_CHANNEL_NUM ) == SET )
    {
        GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer ); 
        GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE );
        
        lcd_write_completed();
        
        GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE );
    }
}

static void lcd_config_gpio(void)
{
    /* module init */
    RCC_PeriphClockCmd(APBPeriph_FLASH2, APBPeriph_FLASH2_CLOCK, (FunctionalState)ENABLE);

    Pinmux_Config(P4_3, IDLE_MODE); //CS
    Pinmux_Config(P4_0, IDLE_MODE); //CLK
    Pinmux_Config(P4_2, IDLE_MODE); //D0
    Pinmux_Config(P4_1, IDLE_MODE); //D1
    Pinmux_Config(P0_6, IDLE_MODE); //D2
    Pinmux_Config(P0_7, IDLE_MODE); //D3

    Pad_Config(P4_3, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pad_Config(P4_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pad_Config(P4_2, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pad_Config(P4_1, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pad_Config(P0_6, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pad_Config(P0_7, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW);

    /* spic2 enable controller */
    SYSBLKCTRL->u_210.BITS_210.BIT_SOC_FLASH_EN2 = 1;
    /* spic2 master enable */
    SYSBLKCTRL->u_2A8.BITS_2A8.SPIC2_MASTER_EN = 1;

    /* RL6660_ECO_029 : Add option for SPIC2 TX DMA handshake signal */
    HAL_WRITE32(PERIPH_REG_BASE, 0x300, HAL_READ32(PERIPH_REG_BASE, 0x300) | BIT31);

    /* set cs and baudrate */
    SPIC2->ssienr = 0;//zhao disable qspi
    SPIC2->ser = BIT0;//zhao select cs0 slave device
    SPIC2->baudr = 1;

    /* ctrlr2[17:16] = 2'b11 to remove WREN+RDSR (i.e., only PP cmd issue) */
    SPIC2->ctrlr2 |= (BIT16 | BIT17);
}

void lcd_gdma_init(void)
{
#if 1
    RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = LCD_DMA_CHANNEL_IRQ;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef GDMA_InitStruct;
    GDMA_StructInit(&GDMA_InitStruct);
    GDMA_InitStruct.GDMA_ChannelNum          = LCD_DMA_CHANNEL_NUM;

    GDMA_InitStruct.GDMA_SourceDataSize      = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_SourceMsize         = GDMA_Msize_1;
    GDMA_InitStruct.GDMA_DestinationMsize    = GDMA_Msize_1;

    GDMA_InitStruct.GDMA_DestHandshake       = GDMA_Handshake_SPIC2_TX;
    GDMA_InitStruct.GDMA_DIR                 = GDMA_DIR_MemoryToPeripheral;
    GDMA_InitStruct.GDMA_SourceInc           = DMA_SourceInc_Inc;
    GDMA_InitStruct.GDMA_DestinationInc      = DMA_DestinationInc_Fix;

    GDMA_Init(LCD_DMA_CHANNEL_INDEX, &GDMA_InitStruct);
    GDMA_INTConfig(LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);
    
#else
    
    RCC_PeriphClockCmd( APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE); 
    GDMA_Cmd( LCD_DMA_CHANNEL_NUM, DISABLE ); 
    
    NVIC_InitTypeDef nvicInitStruct;
    nvicInitStruct.NVIC_IRQChannel = LCD_DMA_CHANNEL_IRQ;
    nvicInitStruct.NVIC_IRQChannelPriority = 3; 
    nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &nvicInitStruct ); 
    
    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef gdmaInitStruct;
    GDMA_StructInit( &gdmaInitStruct ); 
    gdmaInitStruct.GDMA_ChannelNum                  = LCD_DMA_CHANNEL_NUM;
    gdmaInitStruct.GDMA_DIR                         = GDMA_DIR_MemoryToPeripheral;
    gdmaInitStruct.GDMA_BufferSize                  = 240*240*2;
    gdmaInitStruct.GDMA_SourceInc                   = DMA_SourceInc_Inc;
    gdmaInitStruct.GDMA_DestinationInc              = DMA_DestinationInc_Fix;
    gdmaInitStruct.GDMA_SourceDataSize              = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_DestinationDataSize         = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_SourceMsize                 = GDMA_Msize_1;
    gdmaInitStruct.GDMA_DestinationMsize            = GDMA_Msize_1;
    gdmaInitStruct.GDMA_SourceAddr                  = 0;
    gdmaInitStruct.GDMA_DestinationAddr             = (uint32_t)(LCD_SPI_BUS->DR);
    gdmaInitStruct.GDMA_DestHandshake               = LCD_DMA_HANDSHAKE;
    
    GDMA_Init( LCD_DMA_CHANNEL_INDEX, &gdmaInitStruct );
    GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE );
    GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer ); 
    GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE ); 
    
    RCC_SPIClockConfig( LCD_SPI_BUS, ENABLE );
#endif
}

void em_lcd_enter_dlps(void)
{
//    Pad_Config( LCD_SPI_CS_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
//    Pad_Config( LCD_SPI_CLK_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
//    Pad_Config( LCD_SPI_MOSI_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
//    
//    Pad_Config( LCD_RS_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
}
void em_lcd_exit_dlps(void)
{
//    Pad_Config( LCD_SPI_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_LOW ); 
//    Pad_Config( LCD_SPI_CLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH ); 
//    Pad_Config( LCD_SPI_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH ); 
//    
//    Pad_Config( LCD_RS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW );    
}


DATA_RAM_FUNCTION
void lcd_dma_send(uint8_t* source_buf, uint32_t size)
{
    lcd_wrctl.isbusy = true;
    GDMA_SetBufferSize(LCD_DMA_CHANNEL_INDEX, size >> 2);
    GDMA_SetDestinationAddress(LCD_DMA_CHANNEL_INDEX, (uint32_t)(&(SPIC2->dr[0].word)));
    GDMA_SetSourceAddress(LCD_DMA_CHANNEL_INDEX, (uint32_t)(source_buf));
    GDMA_Cmd(LCD_DMA_CHANNEL_NUM, ENABLE);
}

// #pragma push
// #pragma O0
// DATA_RAM_FUNCTION
// void em_lcd_wait_transfer_done(void)
// {
// //    if( m_lcd_write.state == fsmLCDUnderTransfering )
//     if( lcd_wrctl.isbusy )
//     {
//         //uint32_t timeout = 50000;
//         while( GDMA_GetTransferINTStatus( LCD_DMA_CHANNEL_NUM ) != SET );// && (timeout --) ); 
//         GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer );
// //        m_lcd_write.state = fsmLCDTransferDone;
//         lcd_wrctl.isbusy = false;
//     }
// }
// #pragma pop 

void lcd_common_init(void)
{
    memset( (uint8_t*)&lcd_wrctl, 0, sizeof(lcd_wrctl) );
    {
        lcd_config_gpio();
    

    }
}

void em_lcd_init_dlps(void)
{
    lcd_dlps_index = xDLPS_register( &lcd_cbs );
    LOG1("[LCD]: DLPS index = %d\r\n", lcd_dlps_index );    
}

static void lcd_dma_clear(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd,
                          uint16_t color)
{
#if 1
    static uint32_t color_buf = 0;
    color_buf = (color >> 8) | (color << 8);
    color_buf = color_buf | color_buf << 16;
    RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = LCD_DMA_CHANNEL_IRQ;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef GDMA_InitStruct;
    GDMA_StructInit(&GDMA_InitStruct);
    GDMA_InitStruct.GDMA_ChannelNum          = LCD_DMA_CHANNEL_NUM;

    GDMA_InitStruct.GDMA_SourceDataSize      = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Word;
    GDMA_InitStruct.GDMA_SourceMsize         = GDMA_Msize_1;
    GDMA_InitStruct.GDMA_DestinationMsize    = GDMA_Msize_1;

    GDMA_InitStruct.GDMA_DIR                 = GDMA_DIR_MemoryToPeripheral;
    GDMA_InitStruct.GDMA_SourceInc           = DMA_SourceInc_Fix;
    GDMA_InitStruct.GDMA_DestinationInc      = DMA_DestinationInc_Fix;
    GDMA_InitStruct.GDMA_DestHandshake       = GDMA_Handshake_8080_TX;

    GDMA_Init(LCD_DMA_CHANNEL_INDEX, &GDMA_InitStruct);
    GDMA_INTConfig(LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);

    em_lcd_set_window(xStart, yStart, xEnd-xStart, yEnd-yStart);
    for (uint8_t i = 0; i < GUI_FRAME_BUFFER_REQ_NUMS - 1; i++)
    {
        lcd_dma_send((uint8_t *)&color_buf,
                                   GUI_LCD_RES_H * GUI_FRAME_BUFFER_ROWS * GUI_LCD_PIXEL_BYTES);
        em_lcd_wait_transfer_completed();
    }
    uint32_t last_len = 0;
    if (GUI_LCD_RES_V % GUI_FRAME_BUFFER_ROWS == 0)
    {
        last_len = GUI_FRAME_BUFFER_ROWS * GUI_LCD_RES_H * GUI_LCD_PIXEL_BYTES;
    }
    else
    {
        last_len = (GUI_LCD_RES_V % GUI_FRAME_BUFFER_ROWS) * GUI_LCD_RES_H * GUI_LCD_PIXEL_BYTES;
    }
    lcd_dma_send((uint8_t *)&color_buf, last_len);
    em_lcd_wait_transfer_completed();
#else
    color = RED;
    platform_delay_ms(10);
    uint32_t tmp_color = 0;
    tmp_color = (color >> 8) | (color << 8);
    tmp_color = tmp_color | tmp_color << 16;

    rtl_gui_lcd_sectionconfig(xStart, yStart, xEnd, yEnd);
    for (uint32_t i = 0; i < (LCD_WIDTH * LCD_HIGHT * 2) / 4; i++)
    {
        while ((SPIC2->sr & BIT1) == 0x00);
        SPIC2->dr[0].word = tmp_color;
    }
    SPIC2->ssienr = 0x00;//disable SPIC2
#endif

}


void lcd_qspi_clear(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd, uint16_t color)
{
    lcd_dma_clear(xStart, yStart, xEnd, yEnd, color);
}

#endif // !MODULE_LCD_ENABLE

